package cate.game.role.res.farm;

import cate.common.table.d.*;
import cate.common.table.res.farm.row.FarmSeedLevelRow;
import cate.common.table.res.farm.row.FarmSeedRow;
import cate.common.util.GameResult;
import cate.common.util.TimeTool;
import cate.game.chat.ChatContentParam;
import cate.game.res.MixRes;
import cate.game.res.MixResAdder;
import cate.game.res.MixResItem;
import cate.game.role.FuncOpenChecker;
import cate.game.role.Role;
import cate.game.role.res.farm.msg.FarmsDataResp;
import com.fasterxml.jackson.annotation.JsonIgnore;
import easy.java.dev.note.NoteClass;
import easy.java.dev.note.NoteField;
import org.apache.commons.lang3.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

@NoteClass("农场")
public class MyFarms extends FuncOpenChecker {
    @NoteField("地块<位置，数据>")
    public Map<Byte, FarmGround> grounds;

    @NoteField("种子 数据<类型，数据>")
    public Map<Byte, FarmSeed> seeds;

    @NoteField("预设 每个地块的种子数据<位置，种子<类型，数据>>")
    public Map<Byte, Map<Byte, FarmSeed>> preSeeds;

    @NoteField("今日种子种植次数")
    public Map<Byte, Integer> seedTimes;

    @NoteField("特权数据")
    public FarmVip vip;

    @Override
    public void initialize(Role role) {
        super.initialize(role);
        if (grounds == null) {
            grounds = new HashMap<>();
        }
        if (seeds == null) {
            seeds = new HashMap<>();
        }
        if (preSeeds == null) {
            preSeeds = new HashMap<>();
        }
        if (seedTimes == null) {
            seedTimes = new HashMap<>();
        }
        if (vip == null) {
            vip = new FarmVip();
        }
    }

    @Override
    public void onEnter() {
        recordTask();
    }

    public void recordTask() {
        Map<Integer, Integer> map = new HashMap<>();
        for (FarmGround ground : grounds.values()) {
            for (int level = 1; level <= ground.groundLevel; ++ level) {
                map.put(level, map.getOrDefault(level, 0) + 1);
            }
        }
        for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
            role.getHistory().action.farmGroundLevelNum(entry.getKey(), entry.getValue());
        }
    }

    public void tick() {
        long count = grounds.values().stream().filter(e -> e.prePlantCheck(role, vip())).count();
        if (vip.checkVip() || count > 0) {
            noticeUpdate();
            if (count > 0) {
                role.getHistory().action.farmPlantTimes((int) count);
            }
        }
    }

    @Override
    public int getFuncId() {
        return GDFunc.FARMS;
    }

    @Override
    public void onAfterOpen() {
        initGroundAndSeeds();
    }

    @Override
    public void onDaySpan(boolean silence) {
        seedTimes.clear();
        vip.onDaySpan(role);
        if (!silence) {
            noticeUpdate();
        }
    }

    public void initGroundAndSeeds() {
        checkGroundOpen();
        checkSeeds();
        checkPreSeeds();
        noticeUpdate();
    }

    private void checkPreSeeds() {
        for (Map.Entry<Byte, FarmSeed> seedEntry : seeds.entrySet()) {
            for (Map.Entry<Byte, FarmGround> groundEntry : grounds.entrySet()) {
                Map<Byte, FarmSeed> preSeedMap = preSeeds.computeIfAbsent(groundEntry.getKey(), e -> new HashMap<>());
                if (!preSeedMap.containsKey(seedEntry.getKey())) {
                    preSeedMap.put(seedEntry.getKey(), new FarmSeed(seedEntry.getKey(), 1));
                }
            }
        }
    }

    private void checkSeeds() {
        List<FarmSeedRow> list = role.getGame().table.farm.seed.getList();
        for (FarmSeedRow row : list) {
            if (seeds.containsKey(row.type)) {
                continue;
            }
            seeds.put(row.type, new FarmSeed(row.type, 1));
        }
    }

    private void checkGroundOpen() {
        List<Integer> list = GDFarm.GROUND_OPEN_LEVELS;
        for (int i = 0; i < list.size(); ++i) {
            byte pos = (byte) (i + 1);
            if (grounds.containsKey(pos) || role.getBase().level < list.get(i)) {
                continue;
            }
            if (GDFarm.VIP_GROUNDS.contains(pos) && !vip()) {
                continue;
            }
            FarmGround farmGround = new FarmGround(pos, 1);
            grounds.put(pos, farmGround);
        }
    }

    public GameResult<Void> groundLevelUp(byte pos) {
        GameResult<Void> r = new GameResult<>();
        FarmGround ground = grounds.get(pos);
        if (ground == null) {
            return r.fail("您还未解锁该地块");
        }
        r = ground.levelUp(role);
        if (r.ok()) {
            noticeUpdate();
            recordTask();
        }
        return r;
    }

    public GameResult<Void> clearCd(byte pos) {
        GameResult<Void> r = new GameResult<>();
        FarmGround ground = grounds.get(pos);
        if (ground == null) {
            return r.fail("您还未解锁该地块");
        }
        r = ground.clearCd(role);
        if (r.ok()) {
            ground.prePlantCheck(role, vip());
            noticeUpdate();
        }
        return r;
    }

    public void prePlantBeginPlant(byte pos, byte seedType) {
        Map<Byte, FarmSeed> farmSeedMap = preSeeds.get(pos);
        if (farmSeedMap != null) {
            FarmSeed farmSeed = farmSeedMap.get(seedType);
            if (farmSeed != null) {
                farmSeed.reset();
            }
        }
    }

    private boolean seedTimesFull(byte seedType, int times) {
        return seedTimes.getOrDefault(seedType, 0) + grounds.values().stream().filter(e -> e.preSeedType == seedType).count() + times > GDFarm.SEED_PLANT_MAX;
    }

    public GameResult<Void> plant(byte pos, byte seedType) {
        GameResult<Void> r = new GameResult<>();
        FarmGround ground = grounds.get(pos);
        if (ground == null) {
            return r.fail("您还未解锁该地块");
        }
        FarmSeed seed = seeds.get(seedType);
        if (seed == null) {
            return r.fail("您还未解锁该种子");
        }
        if (seedTimesFull(seedType, 1)) {
            return r.fail("该种子今日没有种植次数了");
        }
        r = ground.plant(role, seed.seedType, seed.seedLevel, vip());
        if (!r.ok()) {
            return r;
        }
        seed.reset();
        noticeUpdate();
        role.getHistory().action.farmPlantTimes(1);
        return r;
    }

    public void addSeedTimes(byte seedType) {
        seedTimes.put(seedType, seedTimes.getOrDefault(seedType, 0) + 1);
    }

    private MixRes calcSeedCostItem(byte seedType, int beginLevel, int endLevel) {
        MixRes seedCost = new MixRes();
        List<FarmSeedLevelRow> rows = role.getGame().table.farm.seedLevel.getSeedCostRows(seedType, beginLevel, endLevel);
        if (rows.isEmpty()) {
            return seedCost;
        }
        for (FarmSeedLevelRow row : rows) {
            if (StringUtils.isNotBlank(row.levelConsumeItemStr)) {
                seedCost.addList(new MixRes(row.levelConsumeItemStr));
            }
        }
        return seedCost;
    }

    public GameResult<Void> fastPlant(byte seedType, int seedLevel) {
        GameResult<Void> r = new GameResult<>();
        //计算单块地的消耗
        List<FarmGround> plantGrounds = groundsCanPlant(seedType, seedLevel, false);
        if (plantGrounds.isEmpty()) {
            return r.fail("您没有地块可以种植该种子");
        }
        MixRes seedCost = calcFastPlantCost(plantGrounds.size(), seedType, seedLevel);
        if (!seedCost.isEmpty()) {
            GameResult<MixResItem> seedCostRes = seedCost.consume(role);
            if (!seedCostRes.ok()) {
                return r.fail(seedCostRes);
            }
        }
        FarmSeed seed = seeds.get(seedType);
        boolean useSeed = seed != null && seed.seedLevel <= seedLevel;
        if (useSeed) {
            seed.reset();
        }
        plantGrounds.forEach(e -> e.plantDirect(role, seedType, seedLevel, vip()));
        role.getHistory().action.farmPlantTimes(plantGrounds.size());
        noticeUpdate();
        return r;
    }

    private List<FarmGround> groundsCanPlant(byte seedType, int seedLevel, boolean prePlant) {
        //确认要种植的地块数
        int num = 1;
        List<FarmGround> plantGrounds = new ArrayList<>();
        for (FarmGround ground : grounds.values().stream().sorted(Comparator.comparingInt(e -> e.pos)).collect(Collectors.toList())) {
            if (prePlant && ground.preSeedType > 0) {
                continue;
            }
            GameResult<Void> r = ground.plantCheck(role.getGame(), seedType, seedLevel, vip(), prePlant);
            if (r.ok() && !seedTimesFull(seedType, num)) {
                plantGrounds.add(ground);
                ++ num;
            }
        }
        return plantGrounds;
    }

    private MixRes calcFastPrePlantConsume(List<FarmGround> directGrounds, List<FarmGround> resetGrounds, byte seedType, int seedLevel) {
        MixRes seedCost = new MixRes();
        if (!directGrounds.isEmpty()) {
            seedCost = calcSeedCostItem(seedType, 1, seedLevel).multiply(directGrounds.size());
        }
        if (!resetGrounds.isEmpty()) {
            for (FarmGround ground : resetGrounds) {
                Map<Byte, FarmSeed> preSeedMap = preSeeds.get(ground.pos);
                FarmSeed preSeed = preSeedMap.get(seedType);
                if (preSeed.seedLevel < seedLevel) {
                    seedCost.addList(calcSeedCostItem(seedType, preSeed.seedLevel, seedLevel));
                }
            }
        }
        return calcBagItemCost(seedCost);
    }

    private MixRes calcBagItemCost(MixRes seedItemCost) {
        if (!seedItemCost.isEmpty()) {
            byte costItemType = seedItemCost.items.get(0).type;
            int costItemTid = seedItemCost.items.get(0).tid;
            long costItemNum = seedItemCost.items.get(0).num;
            long hasItemNum = role.getBag().getBag(costItemType).count(costItemTid);
            if (hasItemNum < costItemNum) {
                MixRes realConsume = new MixRes();
                realConsume.addList(new MixRes(GDObj.getMixResStr(costItemType, costItemTid, hasItemNum)));
                realConsume.addList(new MixRes(GDObj.getMixResStr(GDObj.Type.NUM, GDObj.Num.M1, (costItemNum - hasItemNum) * GDFarm.SEED_ITEM_EXCHANGE_SCALE)));
                return realConsume;
            }
        }
        return seedItemCost;
    }

    private MixRes calcFastPlantCost(int toPlantNum, byte seedType, int seedLevel) {
        MixRes seedCost = calcSeedCostItem(seedType, 1, seedLevel);
        FarmSeed seed = seeds.get(seedType);
        boolean useSeed = seed != null && seed.seedLevel <= seedLevel;
        if (useSeed) {
            toPlantNum -= 1;
        }
        if (toPlantNum > 0) {
            seedCost.multiply(toPlantNum);
        }
        if (seed != null && seed.seedLevel < seedLevel) {
            MixRes nowSeedCost = calcSeedCostItem(seedType, seed.seedLevel, seedLevel);
            if (!nowSeedCost.isEmpty()) {
                seedCost.addList(nowSeedCost);
            }
        }
        return calcBagItemCost(seedCost);
    }

    public GameResult<Void> seedLevelUp(byte seedType) {
        GameResult<Void> r = new GameResult<>();
        FarmSeed seed = seeds.get(seedType);
        if (seed == null) {
            return r.fail("您还未解锁该种子");
        }
        r = seed.levelUp(role);
        if (r.ok()) {
            noticeUpdate();
        }
        return r;
    }

    public GameResult<Void> gain(byte pos) {
        GameResult<Void> r = new GameResult<>();
        List<FarmGround> toDeals = new ArrayList<>();
        if (pos == 0) {
            toDeals.addAll(grounds.values().stream().filter(e -> e.seedType > 0).collect(Collectors.toList()));
        } else {
            FarmGround ground = grounds.get(pos);
            if (ground == null) {
                return r.fail("您还未解锁该地块");
            }
            toDeals.add(ground);
        }
        if (toDeals.isEmpty()) {
            return r.fail("您没有可以收获的地块");
        }
        MixRes reward = new MixRes();
        Map<Integer, Integer> log = new HashMap<>();
        for (FarmGround toDeal : toDeals) {
            int seedLevel = toDeal.seedLevel;
            GameResult<MixRes> gainRes = toDeal.gain(role.getGame(), vip.vipType);
            if (!gainRes.ok()) {
                role.getGame().notice.message(role, gainRes);
                continue;
            }
            reward.addList(gainRes.data);
            log.put(seedLevel, log.getOrDefault(seedLevel, 0) + 1);
        }
        for (Map.Entry<Integer, Integer> entry : log.entrySet()) {
            role.getHistory().action.farmSeedGain(entry.getKey(), entry.getValue());
        }
        if (!reward.isEmpty()) {
            new MixResAdder()
                    .setSendMailWhenFull(true)
                    .setDialogReward(true)
                    .setRes(reward)
                    .exe(role);
        }
        noticeUpdate();
        return r;
    }

    public void gmClear() {
        grounds.clear();
        seeds.clear();
        preSeeds.clear();
        seedTimes.clear();
        vip = new FarmVip();
        initGroundAndSeeds();
    }

    public void gmVipExpire(String timeStr) {
        try{
            long expireTime = TimeTool.parseTime(timeStr);
            vip.gmSetExpireTime(expireTime);
            noticeUpdate();
        } catch (Exception e) {
            role.getGame().notice.message(role, "设置的格式有问题噢");
        }
    }

    public void gmGroundTime(byte pos, int second) {
        FarmGround ground = grounds.get(pos);
        if (ground == null) {
            role.getGame().notice.message(role, "您还未解锁该地块");
            return;
        }
        long now = System.currentTimeMillis();
        if (now > ground.nextPlantTime) {
            role.getGame().notice.message(role, "无需设置，已经没有cd了");
            return;
        }
        ground.nextPlantTime = now + second * 1000;
        noticeUpdate();
    }

    public void noticeUpdate() {
        role.sendNow(new FarmsDataResp(this));
    }

    private boolean vip() {
        return vip.vip();
    }

    public GameResult<Void> prePlant(byte pos, byte seedType) {
        GameResult<Void> r = new GameResult<>();
        if (!vip()) {
            return r.fail("请先开通月卡");
        }
        FarmGround ground = grounds.get(pos);
        if (ground == null) {
            return r.fail("您还未解锁该地块");
        }
        Map<Byte, FarmSeed> preSeedMap = preSeeds.get(pos);
        if (preSeedMap == null) {
            return r.fail("您还未解锁该地块");
        }
        FarmSeed preSeed = preSeedMap.get(seedType);
        if (preSeed == null) {
            return r.fail("您还未解锁该种子");
        }
        if (seedTimesFull(seedType, 1)) {
            return r.fail("该种子今日没有预设次数了");
        }
        r = ground.prePlant(role, seedType, preSeed.seedLevel, vip());
        if (!r.ok()) {
            return r;
        }
        if (ground.seedType > 0 && ground.preSeedType == 0) {
            role.getHistory().action.farmPlantTimes(1);
            GameResult<MixRes> gainRes = ground.gain(role.getGame(), vip.vipType);
            if (gainRes.ok() && !gainRes.data.isEmpty()) {
                new MixResAdder()
                        .setSendMailWhenFull(true)
                        .setDialogReward(true)
                        .setRes(gainRes.data)
                        .exe(role);
            }
        }
        noticeUpdate();
        return r;
    }

    public GameResult<Void> preSeedLevelUp(byte pos, byte seedType) {
        GameResult<Void> r = new GameResult<>();
        FarmGround ground = grounds.get(pos);
        if (ground == null) {
            return r.fail("您还未解锁该地块");
        }
        Map<Byte, FarmSeed> preSeedMap = preSeeds.get(pos);
        if (preSeedMap == null) {
            return r.fail("您还未解锁该地块");
        }
        FarmSeed preSeed = preSeedMap.get(seedType);
        if (preSeed == null) {
            return r.fail("您还未解锁该种子");
        }
        r = preSeed.levelUp(role);
        if (!r.ok()) {
            return r;
        }
        if (ground.preSeedType == seedType) {
            r = ground.prePlant(role, seedType, preSeed.seedLevel, vip());
        }
        noticeUpdate();
        return r;
    }

    public GameResult<Void> fastPrePlant(byte seedType, int seedLevel) {
        GameResult<Void> r = new GameResult<>();
        List<FarmGround> directCostGround = new ArrayList<>();
        List<FarmGround> resetCostGround = new ArrayList<>();
        for (FarmGround ground : groundsCanPlant(seedType, seedLevel, true)) {
            Map<Byte, FarmSeed> preSeedMap = preSeeds.get(ground.pos);
            if (preSeedMap != null && preSeedMap.containsKey(seedType)) {
                FarmSeed preSeed = preSeedMap.get(seedType);
                boolean useSeed = preSeed != null && preSeed.seedLevel > 1;
                if (useSeed) {
                    resetCostGround.add(ground);
                } else {
                    directCostGround.add(ground);
                }
            }
        }
        //计算单块地的消耗
        if (resetCostGround.isEmpty() && directCostGround.isEmpty()) {
            return r.fail("您没有地块可以预设该种子");
        }
        MixRes seedCost = calcFastPrePlantConsume(directCostGround, resetCostGround, seedType, seedLevel);
        if (!seedCost.isEmpty()) {
            GameResult<MixResItem> seedCostRes = seedCost.consume(role);
            if (!seedCostRes.ok()) {
                return r.fail(seedCostRes);
            }
        }
        int plantDirectNum = 0;
        for (FarmGround ground : directCostGround) {
            Map<Byte, FarmSeed> preSeedMap = preSeeds.get(ground.pos);
            if (preSeedMap != null && preSeedMap.containsKey(seedType)) {
                FarmSeed preSeed = preSeedMap.get(seedType);
                if (preSeed != null) {
                    preSeed.seedType = seedType;
                    preSeed.seedLevel = seedLevel;
                }
            }
            ground.prePlantDirect(role, seedType, seedLevel, vip());
            if (ground.seedType > 0 && ground.preSeedType == 0) {
                plantDirectNum ++;
            }
        }
        for (FarmGround ground : resetCostGround) {
            Map<Byte, FarmSeed> preSeedMap = preSeeds.get(ground.pos);
            if (preSeedMap != null && preSeedMap.containsKey(seedType)) {
                FarmSeed preSeed = preSeedMap.get(seedType);
                if (preSeed != null) {
                    ground.prePlantDirect(role, seedType, preSeed.seedLevel, vip());
                    if (ground.seedType > 0 && ground.preSeedType == 0) {
                        plantDirectNum ++;
                    }
                }
            }
        }
        noticeUpdate();
        if (plantDirectNum > 0) {
            role.getHistory().action.farmPlantTimes(plantDirectNum);
        }
        return r;
    }

    public GameResult<Void> cancelPrePlant(byte pos) {
        GameResult<Void> r = new GameResult<>();
        if (!vip()) {
            return r.fail("请先开通月卡");
        }
        FarmGround ground = grounds.get(pos);
        if (ground == null) {
            return r.fail("您还未解锁该地块");
        }
        if (ground.preSeedType == 0) {
            return r.fail("您未预设该地块");
        }
        Map<Byte, FarmSeed> farmSeedMap = preSeeds.get(pos);
        if (farmSeedMap != null) {
            FarmSeed farmSeed = farmSeedMap.get(ground.preSeedType);
            if (farmSeed != null) {
                farmSeed.seedType = ground.preSeedType;
                farmSeed.seedLevel = ground.preSeedLevel;
            }
        }
        ground.resetPrePlant();
        noticeUpdate();
        return r;
    }

    public GameResult<Void> allowPay(int tid) {
        return vip.allowPay(role.getGame(), tid);
    }

    public void afterPay(int tid, boolean direct) {
        MixRes payReward = new MixRes();
        vip.afterPay(role.getGame(), tid, payReward, direct);
        if (vip.nowVipType() == GDFarm.VipType.FOREVER) {
            grounds.values().stream()
                    .filter(e -> e.groundLevel > 1)
                    .forEach(e -> payReward.addList(e.calcGroundConsume(role.getGame())));
            role.getHistory().action.farmVipContinue();
        } else {
            role.getHistory().action.farmVipOpen();
        }
        if (!payReward.isEmpty()) {
            new MixResAdder()
                    .setOperation(GDOperation.FARM_VIP_BUY)
                    .setOperationSub(tid)
                    .setSendMailWhenFull(true)
                    .setRes(payReward)
                    .setDialogReward(true)
                    .exe(role);
        }
        initGroundAndSeeds();
        List<ChatContentParam> params = new ArrayList<>();
        params.add(ChatContentParam.c(role.getBase().name));
        role.getGame().notice.lampOnline(role, GDNotice.TextId.FARM_VIP, params);
    }

    @JsonIgnore
    public GameResult<Void> getVipDailyReward() {
        GameResult<Void> r = vip.getVipDailyReward(role);
        if (r.ok()) {
            noticeUpdate();
        }
        return r;
    }
}
